/// Gets rid of Carbon compile errors ...
#include "../../Epsilon/window.h"

#include "sector.h"

#include "../../Epsilon/Epsilon3D/vcache.h"
#include "../../Epsilon/Epsilon3D/immediate.h"

#include "render_batch.h"

#include "states.h"

#include "line.h"
#include "room.h"
#include "level.h"

#include "tesselation.h"

#include "util.h"
#include "textures.h"

#include "../../Epsilon/debug.h"

#include "bsp.h" // eTB_Polygon

#include "../console.h" // eTB_Printf

#ifndef min
#define min(x1,x2) (((x1) > (x2)) ? (x2) : (x1))
#endif

#ifndef max
#define max(x1,x2) (((x1) > (x2)) ? (x1) : (x2))
#endif

typedef std::pair<int, int> line_ref_t;
std::vector <line_ref_t> line_refs;

class SectorPrep
{
public:
  SectorPrep (void) {
    line_refs.reserve (MAX_LINES);
  }
};

SectorPrep prep_me;

using namespace Underlight;

Sector::Sector (int id_) : id (id_)
{
  firstline           = NULL;

  render_flags        = 0;

  xanim_top           = 0;
  xanim_bottom        = 0;
  yanim_top           = 0;
  yanim_bottom        = 0;

  floor_bitmap        = 0;
  floor_height        = 0;
  floor_slope_angle   = 0;

  ceiling_bitmap      = 0;
  ceiling_height      = 0;
  ceiling_slope_angle = 0;

  height_offset       = 0;

  flags               = 0;

  floor               = NULL;
  ceil                = NULL;

  simple              = -1;
  nodraw              = -1;
};

template <class vecT>
inline
void
DeleteAll (vecT& vec_)
{
  typename vecT::iterator iter;

  for (iter = vec_.begin ();
         iter != vec_.end ();
           ++iter)
    delete (*iter);

  vec_.clear ();
}

Sector::~Sector (void)
{
  if (floor != NULL) {
    delete floor;
    floor = NULL;
  }
  
  if (ceil != NULL) {
    delete ceil;
    ceil = NULL;
  }

  /*
  if (firstline != NULL) {
    delete firstline; // Perhaps this should be recursive...
    firstline = NULL;
  }
  */

  DeleteAll (walls);

  DeleteAll (batches);
  DeleteAll (picking_batches);

  DeleteAll (contours);

  DeleteAll (flats);

  DeleteAll (actors);
}

size_t
Sector::size (void) const
{
  size_t _size = sizeof (Sector);

  // NOTE: The size (runtime) includes the size of pointers

  if (floor != NULL)
    _size += floor->size ();

  if (ceil != NULL)
    _size += ceil->size ();

  if (firstline != NULL)
    _size += sizeof (Line);//firstline->size ();

  if (flats.size ()) {
    for (unsigned int i = 0; i < flats.size (); i++) {
      _size += flats [i]->size ();
    }
  }

  if (walls.size ()) {
    for (unsigned int i = 0; i < walls.size (); i++) {
      _size += walls [i]->size ();
    }
  }

  if (batches.size ()) {
    for (unsigned int i = 0; i < batches.size (); i++) {
      _size += batches [i]->size ();
    }
  }

  if (picking_batches.size ()) {
    for (unsigned int i = 0; i < picking_batches.size (); i++) {
      _size += picking_batches [i]->size ();
    }
  }

  if (contours.size ()) {
    for (unsigned int i = 0; i < contours.size (); i++) {
      _size += contours [i]->size ();
    }
  }

  if (actors.size ()) {
    for (unsigned int i = 0; i < actors.size (); i++) {
      _size += actors [i]->MemSize ();
    }
  }

  return _size;
}

Sector::Flat::Flat (PrimitiveType type)
{
  primitive_type = type;

  floor = cli_parse::states->render_context->CreateIndexCache ();
  ceil  = cli_parse::states->render_context->CreateIndexCache ();
}

Sector::Flat::~Flat (void)
{
  if (ceil) {
    delete ceil;
    ceil = NULL;
  }

  if (floor) {
    delete floor;
    floor = NULL;
  }
}

size_t
Sector::Flat::size (void) const
{
  size_t _size = sizeof (Flat);

  if (floor != NULL)
    _size += floor->MemSize ();

  if (ceil != NULL)
    _size += ceil->MemSize ();

  return _size;
}

void
Sector::CopyStates (Sector* sector)
{
  id                  = sector->id;

  firstline           = sector->firstline;

  render_flags        = sector->render_flags;

  xanim_top           = sector->xanim_top;
  xanim_bottom        = sector->xanim_bottom;
  yanim_top           = sector->yanim_top;
  yanim_bottom        = sector->yanim_bottom;

  floor_bitmap        = sector->floor_bitmap;
  floor_height        = sector->floor_height;
  floor_slope_angle   = sector->floor_slope_angle;

  ceiling_bitmap      = sector->ceiling_bitmap;
  ceiling_height      = sector->ceiling_height;
  ceiling_slope_angle = sector->ceiling_slope_angle;

  height_offset       = sector->height_offset;

  flags               = sector->flags;

  // Make sure to recalculate these
  simple              = -1;
  nodraw              = -1;
}

int
Sector::NumberOfEdges (void) const
{
  int num = 0;

  Line* line = firstline;

  if (line) {
    do {
      ++num;

      line = line->nextline;
    } while (line);
  }

  return num;
}

bool
SectorInsideSector (Sector* sec1, Sector* sec2)
{
  bool inside = true;

  Underlight::Line* line = sec1->firstline;
  
  if (line) {
    do {
      if ((sec2->PointInside (line->x1 (), line->y1 ()) &&
           sec2->PointInside (line->x2 (), line->y2 ()))) {
        inside = false;
        break;
      }
    } while (line = line->nextline);
  } else {
    return false;
  }

  //if (inside == true)
    //eMessageBox (_T ("Inside"));

  return inside;
}

// A sector is "simple" if no other sectors occupy the
//  sector's surface area.
bool
Sector::IsSimple (LineList* pUsed)
{
  if (simple != -1)
    return (simple > 0);

  // Keep track of lines already used
  //  FindNextLine will oscillate otherwise
  LineList used;

  if (! pUsed)
    pUsed = &used;

  if (NumberOfEdges ()) {
    simple = 1;

    //Line* lastline = NULL;

    float x = firstline->x2 ();
    float y = firstline->y2 ();

    Line* found = firstline;// = FindNextLine (x, y);

    do {
      //lastline = found;

      x = found->x2 ();
      y = found->y2 ();

      pUsed->SetUsed (found);

      found = FindNextLine (x, y, *pUsed, found);
    } while (eSTL::IterValid (found, firstline));

    Line* line = firstline;

    do {
      if (! pUsed->IsUsed (line)) {
        simple = 0;

        Sector* sector = new Sector (id);

        sector->CopyStates (this);

        sector->firstline = line;

        sector->IsSimple (pUsed);

        contours.push_back (sector);

        break;
      }
    } while (line = line->nextline);
  }
  
  return simple;
}

//
// Certain sectors are not intended to be drawn...
//
//  * If this function returns true, we can cull the sector and
//    skip load-time geometry processing.
bool
Sector::IsNoDraw (void)
{
  if (nodraw != -1)
    return (nodraw > 0);

  nodraw = 0;

  if (FloorHt (0.0f,   0.0f)   == CeilHt (0.0f,   0.0f) ||
      FloorHt (100.0f, 100.0f) == CeilHt (100.0f, 100.0f)) {
    nodraw = 1;
//    eTB_Printf ("No Draw: %d\n", id);
    return nodraw;
  }

  // The rest of the tests won't work without a valid firstline.
  if (! firstline) {
    // A sector without a firstline shouldn't be drawn anyway... :)
    nodraw = 1;
    return nodraw;
  }

  if (firstline->fwall_bitmap == 0 && firstline->cwall_bitmap == 0) {
    if (firstline->facingsector == firstline->sector) {
////      eTB_Printf ("Cool\n");
      nodraw = 1;
    }
    if (floor_bitmap == 0 && ceiling_bitmap == 0) {
////      eTB_Printf ("Cool2\n");
      nodraw = 1;
    }
  }

  //if ((firstline->flags & LINE_S_IMPASS)) {
    //nodraw = 1;
  //}

   /// Search for sectors comprised entirely of lindefs flagged as BOUND.
   ///  * They are not to be drawn...
   Line* line = firstline;

   if (line) {
     bool BOUNDING = true;
     do {
       if (! (line->flags & BOUND)) {
         BOUNDING = false;
         break;
       }
    } while (line = line->nextline);

    if (BOUNDING)
    {
 //     if (states->cl_verbose)
        eTB_Printf ("Found a BOUND :: No Draw Sector: %d\n", id);

      nodraw = 1;
    }
  }


  return nodraw;
}

#define COMBINE_TRIANGLE_STRIPS

// Returns true if the surfaces (floor / ceiling) for this sector
// can be merged into a single render batch.
bool
IsCombinable (Sector* sec, Sector::Flat* flat)
{
  // Cannot combine triangle fans or strips
  if (flat->primitive_type != e3dRenderStates::Triangles 
      && flat->primitive_type != e3dRenderStates::TriangleStrip
     ) {
    //
    // TODO: We can actually combine these, but it will take a lot of work
    //
    return false;
  }

  // Cannot combine surfaces unless their textures match
  if (sec->floor_bitmap != sec->ceiling_bitmap)
    return false;

  // Cannot combine animated and non-animated surfaces
  if (sec->IsAnimatedFloor () != sec->IsAnimatedCeiling ())
    return false;

  // Cannot combine animated surfaces with different rates
  if (sec->IsAnimatedLight ()) {
    if (sec->xanim_bottom != sec->xanim_top ||
        sec->yanim_bottom != sec->yanim_top)
      return false;
  }

  return true;
}

void
Underlight::Sector::CachePickingBatches (void)
{
  // Skip non-visible surfaces to speed things up...
  if (IsNoDraw ())
    return;

  Room* room = lvl->RoomFromSector (id);

  std::vector<Sector::Flat*>::const_iterator flat      = flats.begin ();
  std::vector<Sector::Flat*>::const_iterator last_flat = flats.end   ();

  //
  // TODO: Pay attention to animated combined floor / ceiling batches.
  //
  while (eSTL::IterValid (flat, last_flat)) {
    if (! (*flat)->floor->GetSize ()) {
      ++flat;
      continue;
    }

    cli_parse::RenderBatch* floor =
        new cli_parse::RenderBatch (room->vcache, (*flat)->floor);
    cli_parse::RenderBatch* ceil  =
        new cli_parse::RenderBatch (room->vcache, (*flat)->ceil);

    floor->tex_id     = floor_bitmap;
    floor->tex_frames = 0;
    floor->type       = 1; // flat (floor)

    floor->rgbgen = PickingData (room->id, true, id);

    if (IsAnimatedFloor ()) {
      floor->xanim    = xanim_bottom;
      floor->yanim    = yanim_bottom;
      floor->animated = 1;
    } else {
      floor->xanim    = 0;
      floor->yanim    = 0;
      floor->animated = 0;
    }

    ceil->tex_id     = ceiling_bitmap;
    ceil->tex_frames = 0;
    ceil->type       = -1; // flat (ceiling)

    ceil->rgbgen = PickingData (room->id, true, id);

    if (IsAnimatedCeiling ()) {
      ceil->xanim    = xanim_top;
      ceil->yanim    = yanim_top;
      ceil->animated = 1;
    } else {
      ceil->xanim    = 0;
      ceil->yanim    = 0;
      ceil->animated = 0;
    }

    floor->primitive_type = (*flat)->primitive_type;
    ceil->primitive_type  = (*flat)->primitive_type;

    picking_batches.push_back (floor);
    picking_batches.push_back (ceil);

    ++flat;
  }

  std::vector<Sector::Wall*>::const_iterator wall      = walls.begin ();
  std::vector<Sector::Wall*>::const_iterator last_wall = walls.end   ();

  while (eSTL::IterValid (wall, last_wall))
  {
    if (! (*wall)->icache->GetSize ()) {
      ++wall;
      continue;
    }

    cli_parse::RenderBatch* batch =
        new cli_parse::RenderBatch (room->vcache, (*wall)->icache);

    batch->tex_id     = ((*wall)->GetTexture ());
    batch->tex_frames = 0;

    if ((*wall)->IsFloor ())
      batch->type     =  2; // fwall
    else
      batch->type     = -2; // cwall

    batch->animated   = 0;

    batch->rgbgen = PickingData (room->id, false, (*wall)->parent_line->num);

    batch->primitive_type = (cli_parse::states->r_triangle_strips ?
                                 e3dRenderStates::TriangleStrip :
                                 e3dRenderStates::Triangles);

    picking_batches.push_back (batch);

    ++wall;
  }
}

int
Find (std::vector <int>& v, int x)
{
  std::vector <int>::iterator item = v.begin ();
  std::vector <int>::iterator last = v.end   ();

  while (eSTL::IterValid (item, last)) {
    if ((*item) == x)
      return (*item);

    ++item;
  };

  return -1;
}

void
Underlight::Sector::CacheBatches (void)
{
  std::vector<Sector::Wall*>::iterator pwall      = walls.begin ();
  std::vector<Sector::Wall*>::iterator last_pwall = walls.end   ();

  int purged = 0;

  while (eSTL::IterValid (pwall, last_pwall))
  {
    bool purge = false;

    if (! (*pwall)->icache->GetSize ())
      purge = true;

    if ((*pwall)->wall_ht.top.from == (*pwall)->wall_ht.bottom.from &&
        (*pwall)->wall_ht.top.to   == (*pwall)->wall_ht.bottom.to) {
//      eTB_Printf (" >> Purged because wall has no height...\n");
      purge = true;
    }

    if (purge)
    {
      ++purged;

#ifndef WIN32
                   walls.erase (pwall); /* Original, may be invalid... */
#else
      pwall      = walls.erase (pwall); /* Fixed for Win32 */
#endif
      last_pwall = walls.end    ();

      if (pwall == last_pwall)
        break;
    }

    ++pwall;
  }

  if (::states->cl_verbose) {
    ////if (purged != 0)
      ////eTB_Printf ("Purged %d walls\n", purged);
  }

  // Skip non-visible surfaces to speed things up...
  if (IsNoDraw ())
    return;

  Room* room = lvl->RoomFromSector (id);

  std::vector<Sector::Flat*>::const_iterator flat      = flats.begin ();
  std::vector<Sector::Flat*>::const_iterator last_flat = flats.end   ();

  //
  // TODO: Pay attention to animated combined floor / ceiling batches.
  //
  while (eSTL::IterValid (flat, last_flat)) {
    if (! (*flat)->floor->GetSize ()) {
      ++flat;
      continue;
    }

    if (IsCombinable (this, (*flat))) {
      cli_parse::RenderBatch floor_batch (room->vcache, (*flat)->floor);
      cli_parse::RenderBatch ceil_batch  (room->vcache, (*flat)->ceil);

      floor_batch.primitive_type = (*flat)->primitive_type;
      ceil_batch.primitive_type  = (*flat)->primitive_type;

      cli_parse::RenderBatch* combined_batch =
          floor_batch.Combine (ceil_batch);

      if (combined_batch) {
        combined_batch->tex_id     = floor_bitmap;
        combined_batch->tex_frames = 0;
        combined_batch->type       = 1; // flat (floor)

        if (IsAnimatedFloor ()) {
          combined_batch->xanim    = xanim_bottom;
          combined_batch->yanim    = yanim_bottom;
          combined_batch->animated = 1;
        } else {
          combined_batch->animated = 0;
        }

        if (IsAnimatedLight ())
          combined_batch->animated |= 2;

        combined_batch->surface = this->floor;
        combined_batch->sector  = this;
        combined_batch->primitive_type = (*flat)->primitive_type;

        batches.push_back (combined_batch);
      }
    } else {
      cli_parse::RenderBatch* floor =
          new cli_parse::RenderBatch (room->vcache, (*flat)->floor);
      cli_parse::RenderBatch* ceil  =
          new cli_parse::RenderBatch (room->vcache, (*flat)->ceil);

      floor->tex_id     = floor_bitmap;
      floor->tex_frames = 0;
      floor->type       = 1; // flat (floor)

      if (IsAnimatedFloor ()) {
        floor->xanim    = xanim_bottom;
        floor->yanim    = yanim_bottom;
        floor->animated = 1;
      } else {
        floor->xanim    = 0;
        floor->yanim    = 0;
        floor->animated = 0;
      }

      if (IsAnimatedLight ())
        floor->animated |= 2;

      ceil->tex_id     = ceiling_bitmap;
      ceil->tex_frames = 0;
      ceil->type       = -1; // flat (ceiling)

      if (IsAnimatedCeiling ()) {
        ceil->xanim    = xanim_top;
        ceil->yanim    = yanim_top;
        ceil->animated = 1;
      } else {
        ceil->xanim    = 0;
        ceil->yanim    = 0;
        ceil->animated = 0;
      }

      if (IsAnimatedLight ())
        ceil->animated |= 2;

      floor->primitive_type = (*flat)->primitive_type;
      ceil->primitive_type  = (*flat)->primitive_type;

      floor->surface = this->floor;
      floor->sector  = this;
      ceil->surface  = this->ceil;
      ceil->sector   = this;

      batches.push_back (floor);
      batches.push_back (ceil);
    }

    ++flat;
  }

  std::vector<Sector::Wall*>::const_iterator wall      = walls.begin ();
  std::vector<Sector::Wall*>::const_iterator last_wall = walls.end   ();

  bool all_same   = true;
  int  tex        = 0;
  int  tex_frames = 0;

  std::vector <int> textures;
  std::vector <Sector::Wall*> tex_walls;

  std::vector <int> used_textures;

  int tex_idx = 0;
  int combineable_walls = 0;

  if (wall != last_wall)
    tex = (*wall)->GetTexture ();

  while (eSTL::IterValid (wall, last_wall))
  {
    if (Find (textures, (*wall)->GetTexture ()) == -1) {
      tex = (*wall)->GetTexture ();

      textures.push_back  (tex);
      tex_walls.push_back ((*wall));

      std::vector<Sector::Wall*>::const_iterator wall_ = wall + 1;
      while (eSTL::IterValid (wall_, last_wall))
      {
        if ((*wall_)->GetTexture () == textures.back ()) {
          tex_walls.push_back ((*wall_));
          combineable_walls++;
        }

        ++wall_;
      }

      std::vector<Sector::Wall*>::const_iterator tex_wall      = tex_walls.begin ();
      std::vector<Sector::Wall*>::const_iterator last_tex_wall = tex_walls.end   ();

      Line* line = (*tex_wall)->parent_line;

      tex_frames = ((line->flags & LINE_ANIMATED) ?
          (line->animation_end - line->fwall_bitmap) : 0);

      cli_parse::RenderBatch base (room->vcache, (*tex_wall)->icache);

      cli_parse::RenderBatch* combined = NULL;

      ++tex_wall;

      while (eSTL::IterValid (tex_wall, last_tex_wall))
      {
        if (! (*tex_wall)->icache->GetSize ()) {
          ++tex_wall;
          continue;
        }

        cli_parse::RenderBatch wall_batch (room->vcache, (*tex_wall)->icache);

        if (! combined) {
          combined = base.Combine (wall_batch, false);
        } else {
          combined->Combine (wall_batch, false);
        }

//        num_walls++;
        ++tex_wall;
      }

      if (combined != NULL) {
        combined->tex_id     = tex;
        combined->tex_frames = tex_frames;
        combined->type       = 0; // wall
        combined->animated   = 0;

        if (IsAnimatedLight ())
          combined->animated |= 2;

        tex_wall--;

        // Lines with selfilluminating facingsectors need to be animated as well.
        if ((*tex_wall)->parent_line->facingsector &&
            (*tex_wall)->parent_line->facingsector->flags & SECTOR_SELFILLUMINATING)
          combined->animated |= 2;

        batches.push_back (combined);
      } else {
        tex_wall = tex_walls.begin ();

        Line* line = (*tex_wall)->parent_line;

        // We need to know if this is a floor wall, because only
        //  floor walls can be animated.
        bool floor_wall = (line->fwall_bitmap == (*wall)->GetTexture ());

        if (floor_wall)
          tex_frames = ((line->flags & LINE_ANIMATED) ?
              (line->animation_end - line->fwall_bitmap) : 0);
        else
          tex_frames = 0;

        cli_parse::RenderBatch* batch =
            new cli_parse::RenderBatch (room->vcache, (*tex_wall)->icache);

        batch->tex_id     = ((*tex_wall)->GetTexture ());
        batch->tex_frames = tex_frames;
        batch->type       = 0; // wall
        batch->animated   = 0;

        // Lines with selfilluminating facingsectors need to be animated as well.
        if ((*wall)->parent_line->facingsector &&
            (*wall)->parent_line->facingsector->flags & SECTOR_SELFILLUMINATING)
          batch->animated |= 2;

        if (IsAnimatedLight ())
          batch->animated |= 2;

        batches.push_back (batch);
      }

      tex_walls.clear ();
    }

#if 0
    if ((*wall)->GetTexture () != tex)
      all_same = false;
#endif

    ++wall;
  }

//  if (combineable_walls > 1)
//    eTB_Printf ("Combineable Walls: %d\n", combineable_walls);


#if 0
/// COMBINING BATCHES IS BROKEN FOR NOW...  NEEDS DEBUGGING.
  if (all_same && cli_parse::states->r_combine_batches) {
    if (states->cl_verbose)
      eTB_Printf ("Sector: %d can be combined...\n", id);

    wall = walls.begin ();

    int num_walls = 0; // Number of walls combined

    if (wall != last_wall) {
      Line* line = (*wall)->parent_line;

      tex_frames = ((line->flags & LINE_ANIMATED) ?
          (line->animation_end - line->fwall_bitmap) : 0);

      cli_parse::RenderBatch base (room->vcache, (*wall)->icache);

      cli_parse::RenderBatch* combined = NULL;

      ++wall;

      while (eSTL::IterValid (wall, last_wall))
      {
        if (! (*wall)->icache->GetSize ()) {
          ++wall;
          continue;
        }

        cli_parse::RenderBatch wall_batch (room->vcache, (*wall)->icache);

        if (! combined) {
          combined = base.Combine (wall_batch, false);
        } else {
          combined->Combine (wall_batch, false);
        }

        num_walls++;
        ++wall;
      }

      if (combined != NULL) {
        combined->tex_id     = tex;
        combined->tex_frames = tex_frames;
        combined->type       = 0; // wall
        combined->animated   = 0;

        if (IsAnimatedLight ())
          combined->animated |= 2;

        batches.push_back (combined);

        if (states->cl_verbose)
          eTB_Printf (" >> %d walls combined\n", num_walls);
      }
    }
  } else
  {
    wall = walls.begin ();

    while (eSTL::IterValid (wall, last_wall))
    {
      if (! (*wall)->icache->GetSize ()) {
        ++wall;
        continue;
      }

      Line* line = (*wall)->parent_line;

      // We need to know if this is a floor wall, because only
      //  floor walls can be animated.
      bool floor_wall = (line->fwall_bitmap == (*wall)->GetTexture ());

      if (floor_wall)
        tex_frames = ((line->flags & LINE_ANIMATED) ?
            (line->animation_end - line->fwall_bitmap) : 0);
      else
        tex_frames = 0;

      cli_parse::RenderBatch* batch =
          new cli_parse::RenderBatch (room->vcache, (*wall)->icache);

      batch->tex_id     = ((*wall)->GetTexture ());
      batch->tex_frames = tex_frames;
      batch->type       = 0; // wall
      batch->animated   = 0;

      // Lines with selfilluminating facingsectors need to be animated as well.
      if ((*wall)->parent_line->facingsector &&
          (*wall)->parent_line->facingsector->flags & SECTOR_SELFILLUMINATING)
        batch->animated |= 2;

      if (IsAnimatedLight ())
        batch->animated |= 2;

      batches.push_back (batch);

      ++wall;
    }
  }
  #endif

/*
  std::vector<cli_parse::Actor*>::const_iterator actor      = actors.begin ();
  std::vector<cli_parse::Actor*>::const_iterator last_actor = actors.end ();
*/
}

Underlight::Line*
Sector::FindNextLine (float x, float y, LineList& used, Line* old)
{
  Underlight::Line* found = NULL;
  Underlight::Line* line  = firstline;

  if (line) do {
    if (! used.IsUsed (line)) {
      if (line->sector == old->sector &&
          line->x1 ()  == x &&
          line->y1 ()  == y) {
        if (line != old) {
          found = line;
          break;
        } else {
          eTB_Printf ("Degenerate line...\n");
        }
      }
    }
    line = line->nextline;
  } while (line);

  return found;
}

Underlight::Line*
Sector::FindPrevLine (float x, float y, LineList& used, Line* old)
{
  Underlight::Line* found = NULL;
  Underlight::Line* line  = firstline;

  if (line) do {
    if (! used.IsUsed (line)) {
      if (line->facingsector == old->facingsector &&
          line->x2 ()  == x &&
          line->y2 ()  == y) {
        if (line != old) {
          found = line;
          break;
        } else {
          eTB_Printf ("Degenerate line...\n");
        }
      }
    }
    line = line->nextline;
  } while (line);

  return found;
}

#include "util.h"

bool
Duplicate (Sector* sec1, Sector* sec2)
{
  bool dup = true;

  Underlight::Line* line = sec2->firstline;

  if (line) {
    do {
      if (! sec1->Contains (line))
        return false;

    } while (line = line->nextline);
  }

  return true;
}

int
Sector::Wall::GetTexture (void)  {
  return (is_floor ? parent_line->fwall_bitmap : parent_line->cwall_bitmap);
}

void
Sector::TesselateContour (Line* first)
{
  // Keep track of lines already used
  //  FindNextLine will oscillate otherwise
  LineList used;

  if (NumberOfEdges ()) {
    //Line* lastline = NULL;

    tess::BeginContour ();
    {
      float x = first->x2 ();
      float y = first->y2 ();

      Line* found = first;// = FindNextLine (x, y);

      do {
        //lastline = found;

        tess::AddVertex (found->x1 (), found->y1 ());

        x = found->x2 ();
        y = found->y2 ();

        used.SetUsed (found);

        found = FindNextLine (x, y, used, found);
      } while (eSTL::IterValid (found, first));
    }
    tess::EndContour ();
  }
}


void
Sector::TesselateContours (void)
{
  int num_contours = 0;

  if (contours.size ())
  {
    Sector* sec = this;

    while (sec != NULL) {
      num_contours++;

      if (sec->contours.size ()) {
        sec->Tesselate ();
        sec = sec->contours [0];
      } else {
        if (sec->IsSimple ()) {
          sec->Tesselate ();
        } else {
          eTB_Printf ("Sector: %d is not simple!!!\n", id);
        }

        sec = NULL;
      }
    }

    if (states->cl_verbose)
      eTB_Printf ("Sector: %d has %d additional contours\n", id, num_contours);
  } else {
    Tesselate ();
  }
}

void
Sector::Tesselate (void)
{
  TesselateContour (firstline);
}

void RemovePrimitives (PrimitiveType type)
{
  std::vector <tess::Polygon>::const_iterator poly = tess::GetFirst ();
  std::vector <tess::Polygon>::const_iterator end  = tess::GetLast  ();

  while (eSTL::IterValid (poly, end)) {
    if ((*poly).type () == type) {
      ((tess::Polygon)*poly).clear ();
      //poly = tess::Erase (poly);
    }
    //else
      //++poly;

    ++poly;
  }
}

void
Sector::CalculateSurfaces (bool simple)
{
  // Skip non-visible surfaces to speed things up...
  if (IsNoDraw ())
    return;

  if (cli_parse::states->r_animate) {
    if ( xanim_bottom || 
         yanim_bottom ) {
      render_flags |= SECTOR_ANIMATED_FLOOR;
    }

    if ( xanim_top || 
         yanim_top ) {
      render_flags |= SECTOR_ANIMATED_CEIL;
    }

    if (flags & SECTOR_SELFILLUMINATING)
      render_flags |= SECTOR_ANIMATED_LIGHT;
  }

  IsSimple ();

  tess::Begin ();
  {
    TesselateContours ();
  }
  bool ret = tess::End ();

  if (! ret) {
    eTB_Printf (" >> Tessellation Error : Surface for sector %d [%s] failed!\n", id, (simple ? "Simple Sector" : "Complex Sector"));
  }

  //RemovePrimitives (e3dRenderStates::TriangleFan);
  //RemovePrimitives (e3dRenderStates::TriangleStrip);
  //RemovePrimitives (e3dRenderStates::Triangles);
}

Sector::Wall::Wall (Line* pLine, bool floor)
{
  icache      = cli_parse::states->render_context->CreateIndexCache ();
  parent_line = pLine;
  is_floor    = floor;

  texture       = 0;
  texture_width = texture_height = 0;

  u_length = 0.0f;
  u_max    = 0;
  u_min    = 0;

  u_offset = 0;
  v_offset = 0.0f;

  u_mask = 0;
  v_mask = 0;

  v_stretch_ratio = 0.0f;

  height_origin   = 0.0f;
  from_sec_height = 0.0f;
  to_sec_height   = 0.0f;

  if (pLine) {
    InitTexture ();

//    if (floor) {
//      pLine->num_floor_walls++;
//    }
//    else
//      pLine->num_ceil_walls++;
  }

  polygon = NULL;
}

Sector::Wall::~Wall (void)
{
  if (icache != NULL) {
    delete icache;
    icache = NULL;
  }

  // Keep reference counts accurate...
  if (parent_line) {
//    if (is_floor)    // Should mean the same...
    if (parent_line->fwall == this) {
      parent_line->fwall = NULL;
    }

//    if (! is_floor)  // Should mean the same...
    if (parent_line->cwall == this) {
      parent_line->cwall = NULL;
    }

    if (is_floor)
      parent_line->num_floor_walls = 0;
    else
      parent_line->num_ceil_walls = 0;
  }

  if (polygon != NULL) {
    delete polygon;
    polygon = NULL;
  }
}

size_t
Sector::Wall::size (void) const
{
  size_t _size;

  _size = sizeof (Sector::Wall);

  if (icache != NULL) {
    _size += icache->MemSize ();
  }

  if (parent_line != NULL) {
    _size += sizeof (Line);
  }

  return _size;
}


float
Surface::height_at (float x, float y)
{
  if (! slope)
    return 0.0f;

  if (! baseline)
  {
//    GAME_ERROR(IDS_BASELINE_NOT_SET);
    return 0.0f;
  }

  // sector co-ord system: origin is at baseline line's (x1(),y1()), y axis is parallel to line.
  float sector_cos      = -baseline->SINE;
  float sector_sin      =  baseline->COS;
  float sector_origin_x =  baseline->x1 ();
  float sector_origin_y =  baseline->y1 ();

  // transform to sector co-ords;
  x -= sector_origin_x;
  y -= sector_origin_y;
  float sec_x   =  x*sector_cos + y*sector_sin;
  //float sec_y =  x*sector_sin - y*sector_cos;

  return (sec_x * slope);
}

void
SetVertLineAngles (const VertexData* v0, const VertexData* v1, float& cosine, float& sine)
{
  float lx1,lx2,ly1,ly2;

  lx1 = v0->x;
  lx2 = v1->x;
  ly1 = v0->y;
  ly2 = v1->y;

  float length = sqrt((lx1 - lx2)*(lx1 - lx2) + (ly1 - ly2)*(ly1 - ly2));

  cosine = (lx2 - lx1)/length;
  sine   = (ly2 - ly1)/length;

  return;
}

void
CalcSectorOrigin (const Sector* sec, bool floor, float& sector_origin_x, float& sector_origin_y, float& sector_cos, float& sector_sin)
{
  VertexData* v_from;
  VertexData* v_to;

  if (floor) {
    v_from = sec->FindFloorBaseLine (false, true);

    if (v_from != NULL) {
      v_to = sec->FindFloorBaseLine (false, false);
      sector_origin_x = v_from->x;
      sector_origin_y = v_from->y;
    } else {
      v_from = sec->FindFloorBaseLine (true, false);

      if (v_from != NULL) {
        v_to = sec->FindFloorBaseLine (true, true);
        sector_origin_x = v_from->x;
        sector_origin_y = v_from->y;
        // else use global coordinates, origin = 0
////        eTB_Printf ("Using facing! sector %d\n", -1);
        //DebugOut("Using facing! sector %d\n", ORIGINAL_SECTOR);
      }
    }

    if (v_from != NULL) {
      SetVertLineAngles (v_from, v_to, sector_cos, sector_sin);

      if (sector_cos * sector_sin > 0.0f) { // switch from/to!
        sector_origin_x = v_to->x;
        sector_origin_y = v_to->y;

        SetVertLineAngles (v_to, v_from, sector_cos, sector_sin);
      }
    } else {
////      eTB_Printf ("No facing line found! Sector %d\n", sec->id);
      //DebugOut("No facing line found! Sector %d\n", ORIGINAL_SECTOR);
    }
  } else {
    v_from = sec->FindCeilingBaseLine (false, true);

    if (v_from != NULL) {
      v_to = sec->FindCeilingBaseLine (false, false);
      sector_origin_x = v_from->x;
      sector_origin_y = v_from->y;
    } else {
      v_from = sec->FindCeilingBaseLine (true, false);

      if (v_from != NULL) {
        v_to = sec->FindCeilingBaseLine (true, true);
        sector_origin_x = v_from->x;
        sector_origin_y = v_from->y;
        // else use global coordinates, origin = 0
      }
    }

    if (v_from != NULL) {
      SetVertLineAngles (v_from, v_to, sector_cos, sector_sin);

      if (sector_cos * sector_sin > 0.0f) { // switch from/to!
        sector_origin_x = v_to->x;
        sector_origin_y = v_to->y;

        SetVertLineAngles (v_to, v_from, sector_cos, sector_sin);
      }
    }
  }
}

void
Surface::tex_coord_at ( float         x,
                        float         y,
                        int           tex_id,
                        const Sector* sec,
                        bool          floor,
                        float&        tex_u,
                        float&        tex_v ) const
{
  float sector_cos      = 0.0f;
  float sector_sin      = 1.0f;
  float sector_origin_x = 0.0f;
  float sector_origin_y = 0.0f;

//  if (! floor) {
//    sector_cos = 1.0f;
//    sector_sin = 0.0f;
//  }

#if 0
  if (baseline)
  {
    // sector co-ord system: origin is at baseline line's (x1(),y1()), y axis is parallel to line.
    sector_cos      = -baseline->SINE;
    sector_sin      =  baseline->COS;
    sector_origin_x =  baseline->x1 ();
    sector_origin_y =  baseline->y1 ();
  }
#endif

  // XXX: DEBUG ME
  //
  //  * Don't know why this works, but it does...
  //
  bool baseline_is_generated = false;
  bool sloping               = (floor ? sec->SlopingFloor () : sec->SlopingCeiling ());

  if (baseline) {
    if (floor && baseline != sec->firstline && sec->firstline->flags & LINE_FACING_FLOOR_BASE)
      baseline_is_generated = true;
//    else if (floor && baseline->flags & LINE_FACING_FLOOR_BASE)
//      baseline_is_generated = true;
//    else if ((! floor) && baseline->flags & LINE_FACING_CEILING_BASE && baseline->flags & LINE_SECTOR_CEILING_BASE && baseline != sec->firstline)
//      baseline_is_generated = true;
    if ((! floor) && baseline != sec->firstline && sec->firstline->flags & LINE_FACING_CEILING_BASE)
      baseline_is_generated = true;

  }

//    CalcSectorOrigin (sec, floor, sector_origin_x, sector_origin_y, sector_cos, sector_sin);

  if (baseline_is_generated) {
    sector_cos      = -baseline->SINE;
    sector_sin      = -baseline->COS;
    sector_origin_x =  baseline->x1 ();
    sector_origin_y =  baseline->y1 ();
//    CalcSectorOrigin (sec, floor, sector_origin_x, sector_origin_y, sector_cos, sector_sin);
//    eTB_Printf (" Offsetting Floor Tex Coords for Sector: %d\n", sec->id);
  }


  // Transform to sector co-ords;
  x -= sector_origin_x;
  y -= sector_origin_y;

/// WORKING
  float sec_x = (x * sector_cos) - (y * sector_sin);
  float sec_y = (y * sector_cos) + (x * sector_sin);

/// ORIGINAL FROM Underlight
//  float sec_x = (x * sector_cos) + (y * sector_sin);
//  float sec_y = (x * sector_sin) - (y * sector_cos);


  int tex_w = textures->GetFlat (tex_id).h;
  int tex_h = textures->GetFlat (tex_id).w;

  if (! no_texture_stretch) {
    tex_w *= 2;
    tex_h *= 2;
  }

  tex_v = sec_x / tex_h;
  tex_u = sec_y / tex_w;

  tex_v = -tex_v;
//  tex_u = -tex_u;
}

#if 0
void
Surface::tex_coord_offset_at (float x, float y, int tex_id, Sector* sec, bool floor, float& tex_u, float& tex_v, float x_off, float y_off)
{
  float sector_cos      = 0.0f;
  float sector_sin      = 1.0f;
  float sector_origin_x = 0.0f;
  float sector_origin_y = 0.0f;

  if (! floor) {
    sector_cos = 1.0f;
    sector_sin = 0.0f;
  }

  if (baseline)
  {
    // sector co-ord system: origin is at baseline line's (x1(),y1()), y axis is parallel to line.
    sector_cos      = -baseline->SINE;
    sector_sin      =  baseline->COS;
    sector_origin_x =  baseline->x1 ();
    sector_origin_y =  baseline->y1 ();
  }

  CalcSectorOrigin (sec, floor, sector_origin_x, sector_origin_y, sector_cos, sector_sin);

  // transform to sector co-ords;
  x_off -= sector_origin_x;
  y_off -= sector_origin_y;

  if (baseline->x1 () < baseline->x2 ()) {
    x_off = x_off;
    y_off = -y_off;//0.0f;
    //y_off = -y_off;
  }

  if (baseline->y1 () > baseline->y2 ()) {
    x_off = -x_off;
    y_off = y_off;//0.0f;
  }

  tex_v = ((x_off * sector_cos) - (y_off * sector_sin));
  tex_u = ((y_off * sector_cos) + (x_off * sector_sin));
}
#endif

size_t
Surface::size (void) const
{
  size_t _size = sizeof (Surface);

  if (baseline != NULL)
    _size += sizeof (Line);

  return _size;
}


Underlight::Line*
FindOriginalLine (Underlight::Line* line)
{
  if (line->original_line != NULL) {
    return (line->original_line);
  } else {
    ////eTB_Printf ("Doh!\n");
    return line;
  }
}

typedef std::pair<int, int> line_ref_t;

void
Sector::CalculateWalls (void)
{
  Room* room = lvl->RoomFromSector (id);
  Line* line = firstline;

#if 0
    bool outdoors = (sec->flags & SECTOR_SKY);

    bool dummy_ceiling = false;
    bool dummy_floor   = false;

    if (! (flags & BOUND)) { // Normal Wall (or rather WALLS)
      Sector::Wall* fwall = NULL;
      Sector::Wall* cwall = NULL;

      cwall = new Sector::Wall (line, false);
      fwall = new Sector::Wall (line, true);

      dummy_ceiling = cwall->Calculate (room->vcache);
      dummy_floor   = fwall->Calculate (room->vcache);

      if (! dummy_floor)
        walls.push_back (fwall);
      else
        delete fwall;

      if (! dummy_ceiling)
        walls.push_back (cwall);
      else
        delete cwall;
    } else {
      if ((flags & LINE_S_IMPASS) && outdoors) { // Line is an outdoor boundary 
        //
        // Don't render these, they're more or less hints...
        //
      } else {
        Sector::Wall* wall = new Sector::Wall (line, true);

        bool dummy = wall->Calculate (room->vcache);

        if (! dummy)
          walls.push_back (wall);
        else
          delete wall;
      }
    }
#endif

  if (line) do {
    int flags = line->flags;

    Sector* sec        = this;
    Sector* facing_sec = line->facingsector;

    if (! line->facingsector) {
      eTB_Printf ("Linedef: %d has no facing sector...\n", line->num);
      break;
    }

    bool outdoors = (sec->flags & SECTOR_SKY);

#if 1
    Sector::Wall* fwall = new Sector::Wall (FindOriginalLine (line), true);
    Sector::Wall* cwall = new Sector::Wall (FindOriginalLine (line), false);
#else
    Sector::Wall* fwall = new Sector::Wall (line, true);
    Sector::Wall* cwall = new Sector::Wall (line, false);
#endif

    fwall->Calculate (room->vcache);
    cwall->Calculate (room->vcache);

    walls.push_back (fwall);
    walls.push_back (cwall);

  } while (line = line->nextline);
}

enum WallType
{
  WALL_CEILING,
  WALL_FLOOR,
  WALL_BOUNDING
};

//
// A line's surface (floor / ceiling)'s baseline does not give the same
//  results as FindFloorBaseLine (...) in this algorithm...
//
//  * I don't know why
//    ** Debug this later on
//
void
DebugBaseLineOrigin (Sector* sec, Sector* facing_sec)
{
  float v_origin   = 0.0f;
  float v_origin_x = 0.0f;

  Underlight::Line* baseline = (sec->floor ? sec->floor->Baseline () : NULL);

  if (baseline != NULL) {
    v_origin = sec->FloorHt (baseline->x1 (), baseline->y1 ());
  } else {
    baseline = (facing_sec->floor ? facing_sec->floor->Baseline () : NULL);

    if (baseline != NULL) {
      v_origin = facing_sec->FloorHt (baseline->x1 (), baseline->y1 ());
    } else {
      // no baseline!
      v_origin = Sector::FindHighestFloor (sec, facing_sec);
    }
  }

  v_origin_x = v_origin;

  VertexData* v = sec->FindFloorBaseLine (false);

  if (v != NULL) {
    v_origin = sec->FloorHt (v->x, v->y);
  } else {
    v = facing_sec->FindFloorBaseLine (true);

    if (v != NULL) {
      v_origin = facing_sec->FloorHt (v->x, v->y);
    } else {
      // no baseline!
      v_origin = Sector::FindHighestFloor (sec, facing_sec);
    }
  }

  if (v_origin_x != v_origin) {
    eTB_Printf ("FindFloorBaseLine (...) and Surface::Baseline (...) differ for "
                 "sector: %d...  (%4.2f : %4.2f)\n",
                   sec->id,
                     v_origin_x,
                       v_origin);
  }
}

void
ApplyOffset_V (ul_tex_coords_t* tc, float offset)
{
  if (offset > 50.0f || offset < -50.0f) {
//    if (true || states->cl_verbose)
      eTB_Printf ("Invalid V Offset: %f\n", offset);

//      tc.from.floor.v = 0.0f;
//      tc.to.floor.v   = 0.0f;

//      tc.from.ceil.v  = 0.0f;
//      tc.to.ceil.v    = 0.0f;
    return;
  }

  tc->from.floor.v += offset;
  tc->from.ceil.v  += offset;

  tc->to.floor.v   += offset;
  tc->to.ceil.v    += offset;
}

ul_tex_coords_t
Sector::Wall::CalculateTexCoords (int type)
{
  ul_wall_heights_t height = wall_ht;

  Line* line = parent_line;

  ul_tex_coords_t tc;

  Sector* sec        = line->sector;
  Sector* facing_sec = line->facingsector;

  int   flags    = line->flags;

  // HACK - 6/23/2011
  if (flags & BOUND) {
    type = WALL_FLOOR;
  }

  float length   = 0.0f;

  int  wall_bitmap = 0;

  bool stretch_vert = false;
  bool stretch_horz = false;

  bool flip_vert = false;
  bool flip_horz = false;

  switch (type)
  {
  case WALL_FLOOR:
    wall_bitmap  = line->fwall_bitmap;

    stretch_vert = (flags & LINE_STRETCH_FWALL_VERT);
    stretch_horz = (flags & LINE_STRETCH_FWALL_HORZ);

    flip_vert    = (flags & LINE_FLIP_FWALL_VERT);
    flip_horz    = (flags & LINE_FLIP_FWALL_HORZ);

    length       = (stretch_horz) ? texture_width : line->length;

#ifdef DEBUG_BASELINE_ORIGIN
    DebugBaseLineOrigin (sec, facing_sec);
#endif
    break;
  case WALL_CEILING:
    wall_bitmap  = line->cwall_bitmap;

    stretch_vert = (flags & LINE_STRETCH_CWALL_VERT);
    stretch_horz = (flags & LINE_STRETCH_CWALL_HORZ);

    flip_vert    = (flags & LINE_FLIP_CWALL_VERT);
    flip_horz    = (flags & LINE_FLIP_CWALL_HORZ);

    length       = (stretch_horz) ? texture_width : line->length;
    break;
  case WALL_BOUNDING:
    break;
  default:
    // Invalid wall type...
    Assert (false, _T ("Invalid Wall Type"));
    break;
  }

  if (true) {
    float u_offset = (float)this->u_offset;

    const BITMAPINFO_4DX& tex = textures->GetTexture (wall_bitmap);

    float tex_h = (float)tex.w;
    float tex_w = (float)tex.h;
    //v_offset /= tex_h;
    u_offset /= tex_w;

    float u_max = (float)this->u_max / tex_w;
    float u_min = (float)this->u_min / tex_w;

//    eTB_Printf ("V Stretch Ratio: %f - %d\n", v_stretch_ratio, line->num);
    if (v_stretch_ratio > 9999.0f || v_stretch_ratio < -9999.0f) {
      eTB_Printf ("Strange v stretch ratio: %f, for line: %d\n", v_stretch_ratio, line->num);

      v_stretch_ratio = 0.5f;
//      return tc;
    }

    if (tex_h == 0.0f)
      eTB_Printf ("WTF?!\n");

    const int qqq = 162;

    if (v_stretch_ratio != 0.0f) {
//      eTB_Printf ("Line: %d - Wall: %s - Stretch Ratio: %2.3f, V Offset: %2.3f (%2.3f%%)\n", line->num, (type == WALL_FLOOR ? "floor" : "ceiling"), v_stretch_ratio, v_offset, 100.0f * v_offset / tex.w);
    }

    bool debug_texgen = false;

    if (parent_line->num == cli_parse::states->r_debug_tex_line)
      debug_texgen = true;

    if (debug_texgen) {
      float facing_height = type == WALL_FLOOR ? facing_sec->floor_height :
                                                 facing_sec->ceiling_height;
      float parent_height = type == WALL_FLOOR ? sec->floor_height :
                                                 sec->ceiling_height;

      eTB_Printf (" Texgen Debug (%s Wall)\n"
                  " ------------\n"
                  "\tHeights:\n"
                  "\t From:     [Top: %f - Bottom: %f]\n"
                  "\t To:       [Top: %f - Bottom: %f]\n"
                  "\t Sectors:  [Facing: %f - Parent: %f]\n",
                  type == WALL_FLOOR ? "Floor" : "Ceiling",
                  height.top.from, height.bottom.from,
                  height.top.to,   height.bottom.to,
                  facing_height, parent_height);
      eTB_Printf ("\tTransforms:\n"
                  "\t V Stretch Ratio:  %f\n"
                  "\t V Offset:         %f\n"
                  "\t Stretch Vertical: %s\n"
                  "\t Flip Vertical:    %s\n",
                  v_stretch_ratio,
                  v_offset / tex_h,
                  stretch_vert ? "yes" : "no",
                  flip_vert    ? "yes" : "no");
    }

    float line_v_offset = type == WALL_FLOOR ? parent_line->fwall_v_offset :
                                               parent_line->cwall_v_offset;

/* Allow the various texgen algorithms to be selected at run-time... */
    if (cli_parse::states->r_texgen == -1)
    {
      /*
      if (height.bottom.to > height.top.to) {
        float temp = height.bottom.to;
        height.bottom.to = height.top.to;
        height.top.to    = temp;
        temp = height.bottom.from;
        height.bottom.from = height.top.from;
        height.top.from    = temp;
      }
      */
      //v_stretch_ratio = fabs (v_stretch_ratio);

      bool top_down = (type == WALL_CEILING);

      if (stretch_vert) {
        v_offset = 0.0f;
      }

      if (top_down)
        v_stretch_ratio = fabs (v_stretch_ratio);

      if (height.top.from < height.bottom.from ||
          height.top.to   < height.bottom.to) {
        if (! stretch_vert) {
          top_down = ! top_down;
        } else {
          //top_down = ! top_down;
          top_down = false;
          v_stretch_ratio = fabs (v_stretch_ratio);
          //else
            //v_stretch_ratio = fabs (v_stretch_ratio);
        }
        //v_offset        = -v_offset;
      }

      if (top_down)
        v_stretch_ratio = -fabs (v_stretch_ratio);
          //else
            //v_stretch_ratio = -fabs (v_stretch_ratio);

      if (stretch_vert) {
        top_down = true;
        v_stretch_ratio = fabs (v_stretch_ratio);
      }

      float dv_from = (height.top.from - height.bottom.from);
      float dv_to   = (height.top.to   - height.bottom.to);

      dv_from *= v_stretch_ratio;
      dv_to   *= v_stretch_ratio;

      float line_v_offset = type == WALL_FLOOR ? line->fwall_v_offset :
                                                 line->cwall_v_offset;
      line_v_offset /= tex_h;


      float v_off_from = v_offset + line_v_offset;// + ((height.bottom.from * v_stretch_ratio));// / tex_h;
      float v_off_to   = v_offset + line_v_offset;// + ((height.bottom.to   * v_stretch_ratio));// / tex_h;



      if (top_down) {
        tc.from.floor.v = -(dv_from) / tex_h;
        tc.to.floor.v   = -(dv_to)   / tex_h;

        tc.from.ceil.v  = 0.0f;
        tc.to.ceil.v    = 0.0f;
      } else {
        tc.from.floor.v = 0.0f;
        tc.to.floor.v   = 0.0f;

        tc.from.ceil.v  = -(dv_from) / tex_h;
        tc.to.ceil.v    = -(dv_to)   / tex_h;
      }

      if (parent_line->num == cli_parse::states->r_debug_tex_line) {
        eTB_Printf ("\t  Derived V Offset: [From: %f - To: %f]\n"
                    "\t  Linedef V Offset: %f\n",
                      v_off_from, v_off_to, line_v_offset);
      }

      //if (v_stretch_ratio <= 0.0f) {
        //v_off_from = -v_off_from;
        //v_off_to   = -v_off_to;
      //}

      tc.from.floor.v += v_off_from;
      tc.from.ceil.v  += v_off_from;

      tc.to.floor.v += v_off_to;
      tc.to.ceil.v  += v_off_to;
    }

    else if (cli_parse::states->r_texgen == 0)
{
#if 0
    if (type == WALL_FLOOR) {
    if (height.top.to < height.bottom.to) {
      eTB_Printf ("Hey!\n");
    } else {
      eTB_Printf ("...\n");
    }

    if (height.top.from < height.bottom.from) {
      //eTB_Printf ("Hey2!\n");
    }
    }
#endif

    bool top_down = false;

    if (type == WALL_FLOOR) {
      if (height.top.from < height.bottom.from)
        top_down = true;
    } else {
      if (height.top.from < height.bottom.from)
        top_down = true;
    }

    if (top_down || flip_vert) {
      v_stretch_ratio = -v_stretch_ratio;
    }

  //if (stretch_vert || flip_vert) {
    //top_down = ! top_down;
  //}

if (true) {//type == WALL_FLOOR) {
  float v_off = 0.0f;

  float from_delta = fabs ((height.top.from) - (height.bottom.from));
  float to_delta   = fabs ((height.top.to)   - (height.bottom.to));

  float from_v = (from_delta * v_stretch_ratio) / tex_h;
  float to_v   = (to_delta   * v_stretch_ratio) / tex_h;

  float wall_offset = type == WALL_FLOOR ? parent_line->fwall_v_offset :
                                           parent_line->cwall_v_offset;

  float start_height;

  //if (! top_down)
    //start_height = height.top.from;//sec->floor_height;
  //else
  //if (top_down)
    //start_height = sec->floor_height;
  //else
    //start_height = sec->floor_height;

    /*
  if (top_down) {
    start_height = height.top.from > height.top.to ?
                   height.top.from : height.top.to;
    //start_height = type == WALL_FLOOR ? facing_sec->floor_height :
                                        //facing_sec->ceiling_height;
  } else {
    start_height = height.bottom.from < height.bottom.to ?
                   height.bottom.from : height.bottom.to;
    //start_height = type == WALL_FLOOR ? sec->floor_height :
    //                                    sec->ceiling_height;
  }
    */

  float v_off_to   = 0.0f;
  float v_off_from = 0.0f;

    if (! stretch_vert) {
#if 1
      v_off_to   = fabs (((height.top.to   + wall_offset) * v_stretch_ratio) / tex_h);
      v_off_from = fabs (((height.top.from + wall_offset) * v_stretch_ratio) / tex_h);
#else
      v_off = ((start_height + wall_offset) * v_stretch_ratio) / tex_h;
      v_off_to = v_off;
      v_off_from = v_off;
#endif
      //v_off = v_offset;//(from_sec_height * v_stretch_ratio + v_offset) / tex_h;
    }

    //if (top_down) {
      tc.from.ceil.v  = from_v;
      tc.to.ceil.v    = to_v;
      tc.from.floor.v = 0.0f;
      tc.to.floor.v   = 0.0f;
    //} else {
      //tc.from.floor.v  = from_v;
      //tc.to.floor.v    = to_v;
      //tc.from.ceil.v   = 0.0f;
      //tc.to.ceil.v     = 0.0f;
    //}

  tc.from.ceil.v  += v_off_from;
  tc.from.floor.v += v_off_from;
  tc.to.ceil.v    += v_off_to;
  tc.to.floor.v   += v_off_to;

  /*
  if (top_down) {
    tc.from.ceil.v = -tc.from.ceil.v;
    tc.from.floor.v = -tc.from.floor.v;
    tc.to.ceil.v = -tc.to.ceil.v;
    tc.to.floor.v = -tc.to.floor.v;
  }
  */
}
} else if (cli_parse::states->r_texgen == 1) {
#if 1
  bool facing_above_in = facing_sec->floor_height >= sec->floor_height;
  bool facing_below_in = facing_sec->ceiling_height >= sec->ceiling_height;

if (type == WALL_FLOOR) {
if (! facing_above_in) {
        float v_off = 0.0f;

        if (! stretch_vert) v_off = (to_sec_height * v_stretch_ratio + v_offset) / tex_h;

        tc.from.ceil.v  = (((height.top.from) - (height.bottom.from)) * v_stretch_ratio) / tex_h - v_off;
        tc.to.ceil.v    = (((height.top.to)   - (height.bottom.to))   * v_stretch_ratio) / tex_h - v_off;
        tc.from.floor.v = -v_off;
        tc.to.floor.v   = -v_off;

        if (height.top.from > height.top.to) {
          tc.to.ceil.v = (((height.top.to)  - (height.bottom.to) + fabs (height.top.from - height.top.to)) * v_stretch_ratio) / tex_h - v_off;
          tc.to.floor.v += (fabs (height.top.from - height.top.to) * v_stretch_ratio) / tex_h;
        } else {
          tc.from.ceil.v = (((height.top.from) - (height.bottom.from) + fabs (height.top.to - height.top.from)) * v_stretch_ratio) / tex_h - v_off;
          tc.from.floor.v += (fabs (height.top.to - height.top.from) * v_stretch_ratio) / tex_h;
        }
} else {
        float v_off = 0.0f;

        if (! stretch_vert) v_off = (from_sec_height * v_stretch_ratio + v_offset) / tex_h;
        else v_stretch_ratio = -v_stretch_ratio;


        tc.from.floor.v  = (((height.bottom.from) - (height.top.from)) * v_stretch_ratio) / tex_h - v_off;
        tc.to.floor.v    = (((height.bottom.to)   - (height.top.to))   * v_stretch_ratio) / tex_h - v_off;
        tc.from.ceil.v = -v_off;
        tc.to.ceil.v   = -v_off;

        if (height.bottom.from > height.bottom.to) {
          tc.to.floor.v = (((height.bottom.to)  - (height.top.to) + fabs (height.bottom.from - height.bottom.to)) * v_stretch_ratio) / tex_h - v_off;
          tc.to.ceil.v += (fabs (height.bottom.from - height.bottom.to) * v_stretch_ratio) / tex_h;
        } else {
          tc.from.floor.v = (((height.bottom.from) - (height.top.from) + fabs (height.bottom.to - height.bottom.from)) * v_stretch_ratio) / tex_h - v_off;
          tc.from.ceil.v += (fabs (height.bottom.to - height.bottom.from) * v_stretch_ratio) / tex_h;
        }
}
} else {
if (! facing_below_in) {
        float v_off = 0.0f;

        if (! stretch_vert) v_off = (to_sec_height * v_stretch_ratio + v_offset) / tex_h;
        else v_stretch_ratio = -v_stretch_ratio;


        tc.from.ceil.v  = 0.0f + (((height.top.from) - (height.bottom.from)) * v_stretch_ratio) / tex_h - v_off;
        tc.to.ceil.v    = 0.0f + (((height.top.to)   - (height.bottom.to))   * v_stretch_ratio) / tex_h - v_off;
        tc.from.floor.v = 0.0f - v_off;//v_offset / tex_h;
        tc.to.floor.v   = 0.0f - v_off;//v_offset / tex_h;
} else {
        float v_off = 0.0f;

        if (! stretch_vert) v_off = (from_sec_height * v_stretch_ratio + v_offset) / tex_h;


        tc.from.floor.v  = 0.0f + (((height.bottom.from) - (height.top.from)) * v_stretch_ratio) / tex_h - v_off;
        tc.to.floor.v    = 0.0f + (((height.bottom.to)   - (height.top.to))   * v_stretch_ratio) / tex_h - v_off;
        tc.from.ceil.v = 0.0f - v_off;//v_offset / tex_h;
        tc.to.ceil.v   = 0.0f - v_off;//v_offset / tex_h;
}
}
#else
        tc.from.ceil.v  = 0.0f + (((height.top.from) - (height.bottom.from)) * v_stretch_ratio) / tex_h;
        tc.to.ceil.v    = 0.0f + (((height.top.to)   - (height.bottom.to))   * v_stretch_ratio) / tex_h;
        tc.from.floor.v = 0.0f;
        tc.to.floor.v   = 0.0f;
#endif
}

//tc.from.ceil.v  -= v_offset / tex_h;
//tc.to.ceil.v    -= v_offset / tex_h;
//tc.from.floor.v -= v_offset / tex_h;
//tc.to.floor.v   -= v_offset / tex_h;

        /*
tc.from.ceil.v = -tc.from.ceil.v;
tc.to.ceil.v   = -tc.to.ceil.v;
tc.from.floor.v = -tc.from.floor.v;
tc.to.floor.v = -tc.to.floor.v;
        */

        /*
        if (! flip_vert) {
float temp = tc.from.ceil.v;
tc.from.ceil.v = tc.from.floor.v;
tc.from.floor.v = temp;
temp = tc.to.ceil.v;
tc.to.ceil.v = tc.to.floor.v;
tc.to.floor.v = temp;
        }
        */

    if (flip_horz) {
      tc.to.u   = u_min;
      tc.from.u = u_max;
    } else {
      tc.to.u   = u_max;
      tc.from.u = u_min;
    }
  }

  return tc;
}

void
Sector::Wall::InitTexture (void)
{
  Line* line = parent_line;

  int flags = line->flags;

  bool flip_horz;
  bool flip_vert;

  bool stretch_horz;
  bool stretch_vert;

  bool bound = (flags & BOUND) == 1;

  if (is_floor || bound) {
    flip_horz = (flags & LINE_FLIP_FWALL_HORZ);
    flip_vert = (flags & LINE_FLIP_FWALL_VERT);

    stretch_horz = (flags & LINE_STRETCH_FWALL_HORZ);
    stretch_vert = (flags & LINE_STRETCH_FWALL_VERT);

    u_offset = line->fwall_u_offset;
    v_offset = line->fwall_v_offset;

    texture = line->fwall_bitmap;
  } else {
    flip_horz = (flags & LINE_FLIP_CWALL_HORZ);
    flip_vert = (flags & LINE_FLIP_CWALL_VERT);

    stretch_horz = (flags & LINE_STRETCH_CWALL_HORZ);
    stretch_vert = (flags & LINE_STRETCH_CWALL_VERT);

    u_offset = line->cwall_u_offset;
    v_offset = line->cwall_v_offset;

    texture = line->cwall_bitmap;
  }

  texture_height  = textures->GetTexture (texture).w;
  texture_width   = textures->GetTexture (texture).h;

  u_mask   = texture_width - 1; // Mask to tile texture
  u_length = (stretch_horz) ?  float (texture_width) : line->length;
  u_length--;

  if (flip_horz)
  {
    u_offset += u_mask;
    u_length = -u_length;

    u_max = u_offset;
    u_min = u_max + float2int(u_length);
  }
  else
  {
    u_min = u_offset;
    u_max = u_min + float2int(u_length);
  }

  v_stretch_ratio = 1.0f;

  Sector *from_sec      = line->sector;
  Sector *to_sec        = line->facingsector;

  /* Added 1/15/2010 - Seems to help A LOT w/ ceilings... */
//  if (line->generated) {
//    from_sec = line->facingsector;
//    to_sec   = line->sector;
//  }

  if (stretch_vert) {
    float to_sec_height;

    if (is_floor) {
      from_sec_height = from_sec->floor_height;

      if (bound)
        to_sec_height = from_sec->ceiling_height;
      else if (to_sec->SlopingFloor ())
        to_sec_height = max (to_sec->FloorHt (line->x1 (), line->y1 ()),
                             to_sec->FloorHt (line->x2 (), line->y2 ()));
      else
        to_sec_height = to_sec->floor_height;

      v_stretch_ratio = texture_height / (to_sec_height - from_sec->floor_height);
    } else {
      if (to_sec->SlopingCeiling ())
        to_sec_height = min (to_sec->CeilHt (line->x1 (), line->y1 ()),
                             to_sec->CeilHt (line->x2 (), line->y2 ()));
      else
        to_sec_height = to_sec->ceiling_height;

      v_stretch_ratio = texture_height / (from_sec->ceiling_height - to_sec_height);
    }
  }

  v_mask = texture_height - 1;

  if (is_floor) {
    if (flip_vert) {
      v_stretch_ratio = -v_stretch_ratio;
      height_origin   =  from_sec->floor_height;
    }
    else
      height_origin = to_sec->floor_height;

    v_offset = (height_origin + line->fwall_v_offset)
                          * v_stretch_ratio;
  } else {
    if (flip_vert) {
      v_stretch_ratio = -v_stretch_ratio;
      v_offset = (to_sec->ceiling_height + line->cwall_v_offset) *
                   v_stretch_ratio;
    }
    else
      v_offset = (from_sec->ceiling_height + line->cwall_v_offset) *
                   v_stretch_ratio;
  }
}

void
BuildWallIndices (e3dIndexCache* icache, e3dIndex base)
{
  if (cli_parse::states->r_triangle_strips) {
    icache->Resize   (4);
    icache->SetIndex (0, base + 0); icache->SetIndex (1, base + 1);
    icache->SetIndex (2, base + 3); icache->SetIndex (3, base + 2);
  }

  else {
    icache->Resize   (6);
    icache->SetIndex (0, base + 0); icache->SetIndex (1, base + 1); icache->SetIndex (2, base + 2);
    icache->SetIndex (3, base + 2); icache->SetIndex (4, base + 3); icache->SetIndex (5, base + 0);
  }
}

#include "../../Epsilon/Epsilon3D/vector3.h"

e3dVector3
SectorCenter (Sector* sector)
{
  Underlight::Line* line = sector->firstline;

  float fMinX, fMinY;
  float fMaxX, fMaxY;

  fMinX = 999999.0f;
  fMaxX = -fMinX;

  fMinY = 999999.0f;
  fMaxY = -fMinY;
  
  if (line) {
    do {
      float x1 = line->x1 ();
      float x2 = line->x2 ();

      float y1 = line->y1 ();
      float y2 = line->y2 ();

      fMaxX = max (fMaxX, x1); fMaxX = max (fMaxX, x2);
      fMinX = min (fMinX, x1); fMinX = min (fMinX, x2);

      fMaxY = max (fMaxY, y1); fMaxY = max (fMaxY, y2);
      fMinY = min (fMinY, y1); fMinY = min (fMinY, y2);
    } while (line = line->nextline);
  }

  float fCenterX, fCenterY;

  fCenterX = (fMaxX - fMinX) / 2.0f;
  fCenterY = (fMaxY - fMinY) / 2.0f;

  return e3dVector3 (fCenterX, fCenterY, 0.0f);
}

void
calc_line_equation (Underlight::Line* line, Underlight::Sector* sec, enum WallType type, double& slope, double& height1, double&height2)
{
  float h1;
  float h2;

  switch (type)
  {
    case WALL_FLOOR:
      h1 = sec->FloorHt (line->x1 (), line->y1 ());
      h2 = sec->FloorHt (line->x2 (), line->y2 ());
      break;
    default:
      h1 = sec->CeilHt (line->x1 (), line->y1 ());
      h2 = sec->CeilHt (line->x2 (), line->y2 ());
      break;
  }

  slope = (line->x2 () * h1 - line->x1 () * h2);// * cross_product_inverse;
  height1 = h1;
  height2 = h2;
}


/* NOT CURRENTLY THREAD SAFE... */
eTB_Polygon*
CreateWallPolygon ( float rx1,
                    float ry1,
                    float rx2,
                    float ry2,
                    float light_level,
                    const ul_wall_heights_t& wall_ht,
                    const ul_tex_coords_t&   tex_uv ) {
  eTB_Polygon* poly = new eTB_Polygon ();

  static eTB_Vertex_UL v1;
  static eTB_Vertex_UL v2;
  static eTB_Vertex_UL v3;
  static eTB_Vertex_UL v4;

  v1.setColor (light_level, light_level, light_level);
  v2.setColor (light_level, light_level, light_level);
  v3.setColor (light_level, light_level, light_level);
  v4.setColor (light_level, light_level, light_level);

  v1.setPos       (rx2, ry2, wall_ht.bottom.to);
  v1.setTexCoords (tex_uv.to.u, tex_uv.to.ceil.v);
  v2.setPos       (rx2, ry2, wall_ht.top.to);
  v2.setTexCoords (tex_uv.to.u, tex_uv.to.floor.v);
  v3.setPos       (rx1, ry1, wall_ht.top.from);
  v3.setTexCoords (tex_uv.from.u, tex_uv.from.floor.v);
  v4.setPos       (rx1, ry1, wall_ht.bottom.from);
  v4.setTexCoords (tex_uv.from.u, tex_uv.from.ceil.v);

  poly->addVertex (v1);
  poly->addVertex (v2);
  poly->addVertex (v3);
  poly->addVertex (v4);

  poly->calculatePlane ();

  return poly;
}

#include "../prototype/tangent_space.h"

void
BuildTangentBasis (eTB_Polygon* poly)
{
  eTB_Triangle triangles [2];

  triangles [0].index [0] = 0;
  triangles [0].index [1] = 1;
  triangles [0].index [2] = 2;

  triangles [1].index [0] = 2;
  triangles [1].index [1] = 3;
  triangles [1].index [2] = 0;

  CalculateTangentArray (poly, 2, triangles);
}

void
PolygonToVertexCache (eTB_Polygon* poly, e3dIndex first_vtx, e3dVertexCache* vcache, e3dIndexCache* icache) {
  e3dBegin (vcache);

  e3dPlane plane    = poly->getPlane ();
  e3dVector3 normal = plane.normal;

  // Setup the normals ahead of time, so we can calculate the tangent
  //   space vectors...
  for (int i = 0; i < poly->numVertices (); i++) {
    eTB_Vertex_UL* vtx = (eTB_Vertex_UL *)poly->getVertex (i);

    vtx->setNormal (normal.x, normal.y, normal.z);
  }

  BuildTangentBasis (poly);

  for (int i = 0; i < poly->numVertices (); i++) {
    eTB_Vertex_UL* vtx = (eTB_Vertex_UL *)poly->getVertex (i);

    const e3dVector3& color   = vtx->getColor     ();
    const e3dVector3& pos     = vtx->getPos       ();
    const vec2_t&     tex_uv  = vtx->getTexCoords ();
    const e3dVector3& tangent = vtx->getTangent   ();

// Still experimental as of 7/26/2011
#define DEBUG_TANGENT_SPACE
#ifdef DEBUG_TANGENT_SPACE
    if (tangent.IsZero ())
      eTB_Printf ("Bad Tangent Space Calculation!\n");
#endif

    e3dColor3f    (color.x, color.y, color.z);
    e3dTexCoord2f (tex_uv [0], tex_uv [1]);
    e3dNormal3f   (normal.x, normal.y, normal.z);
    e3dTangent3f  (tangent.x, tangent.y, tangent.z);
    e3dVertex3f   (pos.x, pos.y, pos.z);
  }

  e3dEnd ();

  BuildWallIndices (icache, first_vtx);
}

bool
Sector::Wall::Calculate (e3dVertexCache* vcache)
{
  Line* line = parent_line;

  //
  // 6/23/2011
  //
  // Avoid duplicating walls...
  //
  {
    if (line->generated)
      return false;
    if (is_floor && line->fwall != NULL)
      return false;
    if ((! is_floor) && line->cwall != NULL)
      return false;
  }

  int flags = line->flags;

  Sector* sec        = line->sector;
  Sector* facing_sec = line->facingsector;

  if (! line->facingsector) {
    eTB_Printf ("Linedef: %d has no facing sector...\n", line->num);
    return false;
  }

  int sec_light    = (sec->light_level);
  int sec_to_light = (facing_sec->light_level);

  if (sec_to_light > sec_light) {
    sec_light = sec_to_light;
  }

  bool outdoors   = (sec->flags & SECTOR_SKY);

  bool dummy_ceiling = false;
  bool dummy_floor   = false;

  float rx1 = line->x1 ();
  float ry1 = line->y1 ();
  float rx2 = line->x2 ();
  float ry2 = line->y2 ();

  Sector* from_sec = line->sector;
  Sector* to_sec   = line->facingsector;

  double fwall_top_slope,    fwall_top1,    fwall_top2;
  double fwall_bottom_slope, fwall_bottom1, fwall_bottom2;
  double cwall_top_slope,    cwall_top1,    cwall_top2;
  double cwall_bottom_slope, cwall_bottom1, cwall_bottom2;

//  cross_product_inverse  = 1 / (rx1*ry2 - rx2*ry1); // used in calc_line_equation()

  calc_line_equation (line, from_sec, WALL_FLOOR, fwall_bottom_slope, fwall_bottom1, fwall_bottom2);

  if (! (flags & BOUND)) {
    calc_line_equation (line, to_sec,   WALL_FLOOR,   fwall_top_slope,    fwall_top1,    fwall_top2);
    calc_line_equation (line, from_sec, WALL_CEILING, cwall_top_slope,    cwall_top1,    cwall_top2);
    calc_line_equation (line, to_sec,   WALL_CEILING, cwall_bottom_slope, cwall_bottom1, cwall_bottom2);
  } else {

    // Bounding Wall, goes from the floor to the ceiling
    if (flags & LINE_S_IMPASS && outdoors)
    {
      fwall_top1    = fwall_bottom1;
      fwall_top2    = fwall_bottom2;

      cwall_bottom1 = fwall_top1;
      cwall_bottom2 = fwall_top2;
    }
    else
    {
      calc_line_equation (line, from_sec, WALL_CEILING, fwall_top_slope, fwall_top1, fwall_top2);
      cwall_bottom1 = 0.0f;
      cwall_bottom2 = 0.0f;
    }

    cwall_top1 = fwall_top1;
    cwall_top2 = fwall_top2;
  }

  extern int clamp_light (int);

  ul_tex_coords_t   tex_uv;
//        ul_wall_heights_t wall_ht;
   float light_level = (1.0f - clamp_light (line->sector->light_level) / 50.0f) / cli_parse::states->r_light_scale;

   /* Quadratic Attenuation... */
//   light_level = (light_level * light_level);

  if (! (flags & BOUND)) {

    /* Floor Wall */
    if (is_floor) {
      wall_ht.bottom.from = fwall_bottom1;
      wall_ht.bottom.to   = fwall_bottom2;

      wall_ht.top.from    = fwall_top1;
      wall_ht.top.to      = fwall_top2;

/*
  if (FloorHt (0.0f,   0.0f)   == CeilHt (0.0f,   0.0f) ||
      FloorHt (100.0f, 100.0f) == CeilHt (100.0f, 100.0f)) {
    nodraw = 1;
//    eTB_Printf ("No Draw: %d\n", id);
    return nodraw;
  }
*/
      if (CloseEnough (wall_ht.bottom.from, wall_ht.top.from) &&
          CloseEnough (wall_ht.bottom.to,   wall_ht.top.to)) {
//              eTB_Printf ("Line: %d is a dummy floor wall\n", line->num);

        if (line->from_idx == 1120 || line->from_idx == 1065) {
//        eTB_Printf (" >> [%f, %f] - [%f, %f]\n", wall_ht.bottom.from, wall_ht.top.from, wall_ht.bottom.to, wall_ht.top.to);
          DumpLine (line);
        }

        dummy_floor = true;
        return true;

      } else { line->num_floor_walls++; }

      if (! dummy_floor)
        tex_uv = CalculateTexCoords (WALL_FLOOR);

      if (! is_floor)
        eTB_Printf (" >> Interesting!\n");
    } else {

      /* Ceiling Wall */
      wall_ht.bottom.from = cwall_bottom1;
      wall_ht.bottom.to   = cwall_bottom2;

      wall_ht.top.from    = cwall_top1;
      wall_ht.top.to      = cwall_top2;

      if (CloseEnough (wall_ht.bottom.from, wall_ht.top.from) &&
          CloseEnough (wall_ht.bottom.to,   wall_ht.top.to)) {
//              eTB_Printf ("Line: %d is a dummy ceiling wall\n", line->num);
        dummy_ceiling = true;
        return true;
      } else { line->num_ceil_walls++; }

      if (! dummy_ceiling)
        tex_uv = CalculateTexCoords (WALL_CEILING);

      if (is_floor)
        eTB_Printf (" >> Interesting!\n");
    }
  } else {
    is_floor = true;

    /* Bounding Wall */
    wall_ht.bottom.from = fwall_bottom1;
    wall_ht.bottom.to   = fwall_bottom2;

    wall_ht.top.from    = cwall_top1;
    wall_ht.top.to      = cwall_top2;

    tex_uv = CalculateTexCoords (WALL_FLOOR);

    line->num_floor_walls++;
  }

  eTB_Polygon* poly =
    CreateWallPolygon (rx1, ry1, rx2, ry2, light_level, wall_ht, tex_uv);

  PolygonToVertexCache (poly, (e3dIndex)vcache->GetSize (), vcache, icache);

#define KEEP_POLYS
#ifdef KEEP_POLYS
  polygon = poly;
#else
  delete poly;
#endif

  if (is_floor)
    line->fwall = (Sector::Wall *)this;
  else
    line->cwall = (Sector::Wall *)this;

  return false;
}

VertexData*
Sector::FindFloorBaseLine (bool facing, bool from) const
{
  Line* nextline = firstline;

  while (nextline != NULL) {
    if (facing) {
      if (nextline->flags & LINE_FACING_FLOOR_BASE) {
        if (from)
          return &nextline->from;
        else
          return &nextline->to;
      }
    } else {
      if (nextline->flags & LINE_SECTOR_FLOOR_BASE) {
        if (from)
          return &nextline->from;
        else
          return &nextline->to;
      }
    }

    nextline = nextline->nextline;
  }

  return NULL;
}


float
Sector::FindHighestFloor (Sector* sec1, Sector* sec2)
{
  float      highest = -99999.0f;
  float      ht;
  VertexData v;

  Line* nextline = sec1->firstline;

  while (nextline != NULL) {
    v  = nextline->from;
    ht = sec1->FloorHt (v.x, v.y);

    if (ht > highest)
      highest = ht;

    v  = nextline->to;
    ht = sec1->FloorHt (v.x, v.y);

    if (ht > highest)
      highest = ht;

    nextline = nextline->nextline;
  }

  nextline = sec2->firstline;

  while (nextline != NULL) {
    v  = nextline->from;
    ht = sec2->FloorHt (v.x, v.y);

    if (ht > highest)
      highest = ht;

    v  = nextline->to;
    ht = sec2->FloorHt (v.x, v.y);

    if (ht > highest)
      highest = ht;

    nextline = nextline->nextline;
  }

  return highest;
}

VertexData*
Sector::FindCeilingBaseLine (bool facing, bool from) const
{
  Line* nextline = firstline;

  while (nextline != NULL) {
    if (facing) {
      if (nextline->flags & LINE_FACING_FLOOR_BASE) {
        if (from)
          return &nextline->from;
        else
          return &nextline->to;
      }
    } else {
      if (nextline->flags & LINE_SECTOR_FLOOR_BASE) {
        if (from)
          return &nextline->from;
        else
          return &nextline->to;
      }
    }

    nextline = nextline->nextline;
  }

  return NULL;
}

float
Sector::FindHighestCeiling (Sector* sec1, Sector* sec2)
{
  float      highest = -99999.0f;
  float      ht;
  VertexData v;

  Line* nextline = sec1->firstline;

  while (nextline != NULL) {
    v  = nextline->from;
    ht = sec1->CeilHt (v.x, v.y);

    if (ht > highest)
      highest = ht;

    v  = nextline->to;
    ht = sec1->CeilHt (v.x, v.y);

    if (ht > highest)
      highest = ht;

    nextline = nextline->nextline;
  }

  nextline = sec2->firstline;

  while (nextline != NULL) {
    v  = nextline->from;
    ht = sec2->CeilHt (v.x, v.y);

    if (ht > highest)
      highest = ht;

    v  = nextline->to;
    ht = sec2->CeilHt (v.x, v.y);

    if (ht > highest)
      highest = ht;

    nextline = nextline->nextline;
  }

  return highest;
}


cli_parse::Actor*
Sector::GetActor (int id) const
{
  std::vector<cli_parse::Actor*>::const_iterator actor = actors.begin ();
  std::vector<cli_parse::Actor*>::const_iterator end   = actors.end   ();

  while (eSTL::IterValid (actor, end)) {
    if ((*actor)->GetID () == id)
      return (*actor);

    ++actor;
  }

  return NULL;
}

void
Sector::AddActor (cli_parse::Actor* actor)
{
  if (! GetActor (actor->GetID ()))
    actors.push_back (actor);
}

void
Sector::Unload (void)
{
  /*
  if (floor != NULL) {
    delete floor;
    floor = NULL;
  }
  
  if (ceil != NULL) {
    delete ceil;
    ceil = NULL;
  }
  */

  DeleteAll (walls);

  DeleteAll (batches);
  DeleteAll (picking_batches);

  DeleteAll (contours);

  DeleteAll (flats);

  simple              = -1;
  nodraw              = -1;

  line_refs.clear ();
}



bool
Sector::Contains (Line* line) const
{
  Line* first = firstline;
  Line* found = first;

  if (found) {
    do {
      //num++;

      if (line->num == found->num)
        return true;

      found = found->nextline;
    } while (found);
  }

  return false;
}

bool
Sector::PointInside (float x, float y) const
{
  int num_intersections;

  float x1, y1,
        x2, y2;

  Line* line;

  num_intersections = 0;

  line = firstline;

  if (line)
    do {
      x1 = line->x1 () - x;
      y1 = line->y1 () - y;
      x2 = line->x2 () - x;
      y2 = line->y2 () - y;
      
      if( x1 <   0 && x2 < 0)   continue;
      if( y1 >  x1 && y2 > x2)  continue;
      if( y1 < -x1 && y2 < -x2) continue;
      
      /*
      if( y1 >= 0 && y2 < 0 || y1 < 0 && y2 >= 0)
      {
      // straddles line, lets find X.
      int x_int = x1-((y1*(x2-x1))/(y2-y1));
      if( x_int >= 0.0)
      {
      num_intersections++;
      }
      }
      */
      
      if( y1 >= 0 && y2 < 0 )
      {
        if (x1*(y2-y1) < y1*(x2-x1))
          num_intersections ++;
      }
      else if (y1 < 0 && y2 >= 0)
      {
        if (x1*(y2-y1) >= y1*(x2-x1))
          num_intersections ++;
      }
    } while (line = line->nextline);
    
    return (num_intersections & 1);
}

Underlight::Line*
Sector::GetLine (int id)
{
  Underlight::Line* line = firstline;

  do {
    if (line->num == id)
      return line;
  } while (line = line->nextline);

  return NULL;
}
